//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Diagnostics;
using System.Diagnostics.Contracts;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Internals;

namespace OpenLS.Spreadsheet
{
    using ColumnCollection = DimensionCollection<Column>;

    public class Column: Dimension, IOfficeXmlSerializable
    {
        
        internal Column(Worksheet ws, int i): base (ws)
        {
            Contract.Requires(i >= 0);
            Contract.Requires(i <= Worksheet.MaxColumnIndex);
            Index = i;


        }
        internal override double Offset
        {
            get { return Worksheet.Columns.GetOffset(Index); }
        }
        internal double RightOffset
    {
        get{ return Offset + ActualWidth;}
    }

        internal bool IsEquivalent(Column other)
        {
            if (CharacterWidth != null)
            {
                if (other.CharacterWidth == null)
                    return false;
                if (CharacterWidth.Value != other.CharacterWidth.Value)
                    return false;
            }
            else if (other.CharacterWidth != null)
                return false;
            if (OutlineLevel != other.OutlineLevel)
                return false;
            if (IsCollapsed != other.IsCollapsed)
                return false;
            return FormattingRecord == other.FormattingRecord;

        }
        internal override int MaxValidOffsetIndex
        {
            get
            {
                return Worksheet.Columns.MaxValidOffsetIndex;
            }
            set
            {
                Worksheet.Columns.MaxValidOffsetIndex = value;
            }
        }
        internal Cell this[int i]
        {
            get
            {
                return Worksheet[i, Index];
            }
        }

        private bool _hidden;

        internal override bool IsHidden
        {
            get { return _hidden; }
            set
            {
                if (_hidden == value)
                {
                    return;
                }
                _hidden = value;
                //\\NotifyPropertyChanged("IsHidden");
                Worksheet.Columns.TruncateOffsets(Index, ActualWidth);
                NotifyPropertyChanged("IsVisible");
                NotifyPropertyChanged("ActualWidth");
                Worksheet.Columns.RaiseLayoutChanged(this);
            }
        }
        internal override Dimension GetOffseted(CellRelativeAddress address)
        {
            return Worksheet.Columns[Index + address.Column];
        }
        public static Column operator +(Column This, int offset)
        {
            return This.Worksheet.Columns[This.Index + offset];
        }
        internal const int MaxIndex = Spreadsheet.Worksheet.MaxColumnIndex;
        internal const int MaxSpan = MaxIndex + 1;

        internal override bool IsEmpty
        {
            get
            {
                return Count == 0 && !IsCustom;
            }
        }
        public override double Length
        {
            get { return ActualWidth; }
        }


        internal override string UnsafeLabel
        {
            get
            {
                return ColumnConverter.IndexToName(Index);
            }
        }
	

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            using (ReadContext c = context.Read("todo"))
			{
            Minimum = c.GetInteger("min");
            Maximum = c.GetInteger("max");
            double d = c.GetDouble("width");

            //\\ActualWidth =  PointsToPixelConverter.ToPixel( double.Parse(reader["width"]));
            Width = PointsToPixelConverter.ToPixel(d / 20);
            //\\todo custom width
			}
        }

        public int Minimum { get; set; }

        public int Maximum { get; set; }

        internal double ActualCharacterWidth
        {
            get
            {
                if (CharacterWidth != null)
                    return Math.Floor(CharacterWidth.Value - 0.5); //\\hack
                return 8;
            }
        }

        double? _characterWidth;
        internal double? CharacterWidth
        {
            get
            {
                return _characterWidth;
            }
            set
            {
                if (_characterWidth == value)
                    return;
                using (Worksheet.CreateUndoScope(new ColumnWidthUndoUnit(this)))
                {
                    _characterWidth = value;
                    WidthResultCache = null;
                    RaiseWidthChange();
                }
            }
        }

        double? WidthResultCache;
        ///<summary>
        /// Get or set the de
        ///</summary>
        public double? Width
        {
            get
            {
                if (CharacterWidth == null)
                    return null;
                if (WidthResultCache == null)
                    WidthResultCache = Worksheet.CharacterWidthToPixelWidth(CharacterWidth.Value);
                return WidthResultCache.Value;
            }
            set
            {
#if SUPERDEBUG
CheckInvariant();
#endif
                // using (Worksheet.CreateUndoScope(new ColumnWidthUndoUnit(this)))
                {
                    if (value != null)
                    {
                        double v = value.Value;
                        if (v < 0)
                            value = 0;
                    }
                    var newValue = value/Worksheet.Workbook.MaximumDigitWidth;
                    if (CharacterWidth.GetValueOrDefault(double.MinValue) == newValue)
                        return;
                    CharacterWidth = newValue; //\\
                    WidthResultCache = null;
                    //\\width = value;
                    RaiseWidthChange();
                }
            }
        }
	
        public double ActualWidth
        {
            get
            {
                if (!IsVisible)
                    return 0;
                if (Width != null)
                    return Width.Value;
                return Worksheet.DefaultColumnWidth;
            }
           /* set
            {
                if (value < 0)
                    value = 0;
                width = value;
                FireWidthChange();
            }*/
        }

        internal void RaiseWidthChange()
        {
            NotifyPropertyChanged("Width");
            NotifyPropertyChanged("ActualWidth");
            Worksheet.Columns.TruncateOffsets(Index, ActualWidth);
            Worksheet.Columns.RaiseLayoutChanged(this);
        }

         bool customWidth
        {
            get {
                return _characterWidth != null
                    && _characterWidth.Value != Worksheet.DefaultCharacterWidth; }
        }


        internal class ColumnGroupRead
        {
            private int min;
            private int max;
            private double? _characterWidth;
            private bool _hidden;
            private byte outlineLevel;
            private bool collapsed;
            private CellFormattingRecord _formattingRecord;

            internal void ReadAttributes(ReadContext context, GroupBuilderStack<Column> stack, StyleRecordManager styleRecordManager)
            {
                this.min = context.GetInteger("min");
                this.max = context.GetInteger("max");

                this._characterWidth = context.GetOptionalDouble("width");
                this._hidden = context.GetOptionalBoolean("hidden", false);
                string style = context.GetOptionalString("style");
                if (style != null)
                    this._formattingRecord =(styleRecordManager[int.Parse(style)]);
                this.outlineLevel = (byte)context.GetOptionalInteger("outlineLevel", 0);
                this.collapsed = context.GetOptionalBoolean("collapsed", false);
                //stack.ApplyOutlineLevel(this, outlineLevel, this.IsVisible, collapsed);


            }
            public void Apply (Spreadsheet.ColumnCollection columns, GroupBuilderStack<Column> stack)
            {
                for (int i = min; i <= max; i++)
                {
                    Column column = columns[i - 1];
                    {
                        if (_characterWidth != 0d || outlineLevel == 0)
                            column._characterWidth = this._characterWidth;
                        column.WidthResultCache = null;
                        column._hidden = _hidden;
                            column.SetFormattingRecord(this._formattingRecord);
                        if (column._characterWidth != null)
                            column.RaiseWidthChange();
                    

                    }
                    //\\this.Add(column);
                }
                var firstColumn = columns[min - 1];
                var lastColumn = columns[max - 1];
                stack.ApplyOutlineLevel(firstColumn,  lastColumn,  outlineLevel, firstColumn.IsVisible, collapsed);
                for (int i = min; i <= max; i++)
                {
                    Column column = columns[i - 1];
                    {
                      //  if (column._characterWidth != null)
                            column.RaiseWidthChange();
                    }
                }

            }
        }

	

        public void Add(Cell cell)
        {
#if DEBUG
          /*  if (_cells.ContainsKey(cell.Row.Index))
            {
                Debug.WriteLine("Problem, " + this.Label + " already contains " + cell.Label);
            }*/
#endif
            AddCellToDictionary(cell.Row.Index, cell);
        }
        public void Remove(Cell cell)
        {
            RemoveCellFromDictionary(cell.Row.Index);
        }
        internal void RemoveAt(int index)
        {
            RemoveCellFromDictionary(index);
        }




        internal void WriteAttributes(WriteContext context)
        {
            Contract.Assert(IsCustom);
            //\\double? characterWidth = StyleRecord != null ? 8 : CharacterWidth;
            double? characterWidth = this.CharacterWidth;
            if (characterWidth != null)
                context.SetDouble("width", characterWidth.Value);
            if (FormattingRecord != null)
                context.SetInteger("style", FormattingRecord.Id);
            context.SetOptionalBoolean("hidden", _hidden, false);
            if (characterWidth != null)
                context.SetBoolean("customWidth", true);
            context.SetOptionalBoolean("collapsed", IsCollapsed, false);
            context.SetOptionalInteger("outlineLevel", OutlineLevel, 0);
        }
        internal bool IsCustom
        {
            get
            {
                return customWidth || FormattingRecord != null || OutlineLevel > 0 || IsCollapsed;
            }
        }
        
        internal override bool GetIsSelected()
        {
            if (Worksheet.Selection == null)
                return false;
            return Range.Intersects(Worksheet.Selection, new FullColumnRange(this));
        }

        #region IWithAddress Members

        public CellAddress Address
        {
            get { return new CellAddress(0, Index);}
        }

        #endregion

        ///<summary>
        /// Adjust the width of the column so that it fits its content.
        ///</summary>
        public void AutoFit()
        {
            if (CellCount == 0)
                return;
            double max = Enumerable.Max(
                Enumerable.Select(
                    Enumerable.Where(GetNonEmptyCells(), c => !c.IsVolatile && c.Value != null), c => c.DesiredWidth),
                double.NaN);
            if (double.IsNaN(max))
                return;
            Width = max;
        }

        #region IOfficeXmlSerializable Members


        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            throw new NotSupportedException();
        }

        #endregion

        internal override void OnIsVisibleChanged()
        {
            RaiseWidthChange();
        }

        internal override string LengthSrid
        {
            get { return StringTable.ColumnWidth; }
        }

       
        internal override Dimension GetPrevious()
        {
            return Worksheet.Columns[Index - 1];
        }

        internal static bool ValidIndex(int col1)
        {
            return col1 >= 0 && col1 <= MaxIndex;
        }
    }
}
